import { Size, Asset, Prefab, AudioClip, SpriteFrame } from "cc"
import { initData, CGameData } from "../../../../app/GameDefine"
import { Scene } from "../../../../scene/script/Scene"
import { ISelectionEntityData } from "../../../../scene/script/entity/SelectionEntity"
import { Vector2, BaseModuleEvent, DataLogicHelper, _scene, Rectangle, TResoucesUrl, _ui, _config_, _audio, _timer, _gameType, _login, EUILayer, _prop, Sets, _main, _resouces, TVectorSet, winSize, Maths, _guide, winCenterPostion, _logic } from "../../Main"
import { EGameType, gameTypeLogicModule, gameTypeModule } from "../define/GameTypeDefine"
import { ILogicData, ELogicDataState, CSceneData, EPropId, CCubeSize, CfirsetSame, ILogicNuoData } from "../define/LogicDefine"
import { TenLevelGameType } from "../gameType/TenLevelGameType"
import { CubeEntity } from "../../../../scene/script/entity/CubeEntity"
import { DragEntity } from "../../../../scene/script/entity/DragEntity"


const v2T = new Vector2()
const v2T2 = new Vector2()

export class TenlLogic {

    public cubeByTypes: { [type: number]: number[] } = {}
    /**能合并的id */
    public cubeByMerges: { [configId: number]: number } = {}

    public data: DataLogicHelper<ILogicData> = null!

    public size_bottom = new Vector2()
    public offset_bottom = new Vector2()
    public dragCount = 0
    public removeCount = 0
    public dragConfigId = -1
    public dragCurPos = new Vector2()

    public onceCardIds: number[] = []
    public cardIndexs: number[] = []
    public cardIndexsSame: number[] = []

    private get scene() { return _scene.getCurrent<Scene>() }

    public preLoadRes: TResoucesUrl<Asset>[] = []

    constructor() {
        this.preLoadRes.push(
            { type: Prefab, url: _ui.DefaultUrl.moveUp },
            { type: Prefab, url: CSceneData.cubePrefabUrl },
            { type: Prefab, url: CSceneData.moveAnimPrefabUrl },
            { type: Prefab, url: CSceneData.selectionPrefabUrl },
            { type: Prefab, url: CSceneData.cubeAnimPrefabUrl },
            { type: Prefab, url: CSceneData.removeParticle },
            { type: Prefab, url: CSceneData.tipPrefabUrl },
            { type: Prefab, url: CSceneData.dragPrefabUrl },
            { type: AudioClip, url: "sceneRes/audio/remove" },
        )
        for (let config of _config_.arr.card_item)
            this.preLoadRes.push({ type: SpriteFrame, url: config.icon_url })

        this.data = new DataLogicHelper<ILogicData>(
            () => ({
                dataIndex: -1,
                configId: -1,
                isBottom: false,
                entity: null!,
                index: new Vector2(),
                rect: new Rectangle(),
                state: ELogicDataState.none,
            }),
            (data) => {
                data.dataIndex = -1
                data.configId = -1
                data.entity = null!
                data.state = ELogicDataState.none
            }
        )

        for (let config of _config_.arr.card_item) {
            let data = this.cubeByTypes[config.type]
            if (!data)
                data = this.cubeByTypes[config.type] = []
            data.push(config.id)
        }

        for (let key in this.cubeByTypes) {
            let ids = this.cubeByTypes[key]
            if (Number(key) === 4) {
                for (let v of ids)
                    this.cubeByMerges[v] = v
            }
            else
                for (let v of ids) {
                    for (let _v of ids) {
                        if (_config_.obj.card_item[v].num + _config_.obj.card_item[_v].num == 10) {
                            this.cubeByMerges[v] = _v
                            break
                        }
                    }
                }
        }

    }

    public create(
        bottomCardNum: number,
        isTen: boolean,
        size: TVectorSet,
        size_bottom?: TVectorSet,
    ) {
        _logic.offsetY = 30
        this.data.reset()
        this.onceCardIds.length = 0
        this.cardIndexs.length = 0
        this.cardIndexsSame.length = 0
        this.removeCount = 0

        if (size_bottom)
            this.size_bottom.set(size_bottom)
        else
            this.size_bottom.set()

        // 中间间隔距离
        let space = 30
        _logic._create(size, this.size_bottom.x, this.size_bottom.y, space)

        let sizeAllCount = _logic.size.x * _logic.size.y + this.size_bottom.x * this.size_bottom.y

        if (sizeAllCount == 0)
            return

        // 获取所有牌
        let count = Math.ceil((sizeAllCount + bottomCardNum) / 2) * 2
        this.dragCount = count - sizeAllCount
        this.getCardIds(count / 2, isTen, this.cardIndexs, this.cardIndexsSame)

        if (this.size_bottom.y == 0)
            this.createData(0, _logic.size, this.onceCardIds, this.cardIndexs, _logic.offset)
        else {
            // 上面的
            let topH = _logic.size.y * _logic.cubeSize.height
            let bottomH = this.size_bottom.y * _logic.cubeSize.height
            // 中间间隔距离
            let space = 30
            let allH = topH + bottomH + space
            let topOffsetY = topH / 2 + space + bottomH - allH / 2

            let bottomOffsetY = bottomH / 2 - allH / 2
            this.createData(topOffsetY, _logic.size, this.onceCardIds, this.cardIndexs, _logic.offset)
            this.createData(bottomOffsetY, this.size_bottom, this.onceCardIds, this.cardIndexs, this.offset_bottom)
        }

        Sets.shuffle(this.onceCardIds, true, () => _gameType.curRandom.run())
    }


    public hasResetConfig() {
        // 一上一下
        let cards: ILogicData[] = []
        let backs: ILogicData[] = []
        for (let v of this.data.datas) {
            if (v.state == ELogicDataState.card)
                cards.push(v)
            if (v.state == ELogicDataState.back)
                backs.push(v)

            if (cards.length > 1 || backs.length > 1)
                return true
        }
        if (cards.length == 1 && backs.length == 1)
            return false

        return true
    }

    public resetConfig() {

        let datas: ILogicData[] = []
        let cradStatics: ILogicData[] = []
        let cards: ILogicData[] = []

        for (let v of this.data.datas) {
            if (v.state == ELogicDataState.card) {
                this.cardIndexs.push(v.configId)
                cards.push(v)
            }
            if (v.state == ELogicDataState.cardStatic) {
                this.cardIndexs.push(v.configId)
                cradStatics.push(v)
            }
        }
        Sets.shuffle(cards)
        Sets.shuffle(cradStatics)
        datas.push(...cards, ...cradStatics)

        let onceIds: number[] = []

        for (let v of datas)
            this.getRandomIndexs(onceIds, this.cardIndexs)

        for (let v of datas) {
            v.configId = onceIds.shift()
            v.entity.playResetAnim()
        }

        _logic.palyResetAnim(() => {
            for (let v of datas)
                v.entity.stopResetAnim()
        })
    }

    private createData(
        offsetY: number,
        size: Vector2,
        onceCardIds: number[],
        cardIds: number[],
        offset: Vector2,
    ) {
        _logic.createByOffset(offset, size, offsetY + _logic.offsetY, _logic.cubeSize)

        for (let x = 0; x < size.x; x++) {
            for (let y = 0; y < size.y; y++) {
                let data = this.data.create()
                data.dataIndex = this.getPosByIndex(x, y, size)
                data.index.set(x, y)
                data.isBottom = offset == this.offset_bottom
                this.getIndexByAabbCenter(data)

                if (_gameType.type == EGameType.index) {
                    data.state = ELogicDataState.card
                    data.configId = Sets.random(_config_.arr.card_item).id
                }
                else {
                    data.configId = -1
                    data.state = ELogicDataState.back
                }
            }

            // 添加第一次展示的牌
            // 保证有1~3对可消除
            this.getRandomIndexs(onceCardIds, cardIds)
        }
    }

    private getRandomIndexs(onceCardIds: number[], ids: number[]) {
        let isRandomAdd = false

        if (onceCardIds.length > 0) {

            let count = 0
            for (let i = 0; i < onceCardIds.length; i++)
                for (let j = 0; j < onceCardIds.length; j++) {
                    if (i == j)
                        continue
                    if (this.hasSame(onceCardIds[i], onceCardIds[j])) {
                        count++
                        break
                    }
                }

            if (count < 1) {
                // 添加指定的
                for (let id of onceCardIds) {
                    for (let i = 0; i < ids.length; i++) {
                        if (this.hasSame(id, ids[i])) {
                            onceCardIds.unshift(ids.splice(i, 1)[0])
                            return
                        }
                    }
                }
            }
            else
                isRandomAdd = true
        }
        else
            isRandomAdd = true

        if (isRandomAdd)
            onceCardIds.push(ids.splice(Maths.zeroToMax(ids.length, _gameType.curRandom.run()), 1)[0])

    }

    public propRemove() {

        this.scene.clearTips()
        this.scene.guide()

        let sames = this.getSames(1)
        if (sames.length > 0) {
            this.scene.ten.remove(sames[0][0], sames[0][1])
            return
        }

        let backs: ILogicData[] = []
        this._propRemove(_logic.size, backs, ELogicDataState.back)
        this._propRemove(this.size_bottom, backs, ELogicDataState.back)

        let cards: ILogicData[] = []
        this._propRemove(_logic.size, cards, ELogicDataState.card)
        this._propRemove(this.size_bottom, cards, ELogicDataState.card)


        let back = Sets.random(backs)
        let card = Sets.random(cards)

        let configId = -1
        for (let i = 0; i < this.cardIndexs.length; i++)
            if (this.hasSame(card.configId, this.cardIndexs[i])) {
                configId = this.cardIndexs.splice(i, 1)[0]
                break
            }
        if (configId == -1)
            for (let i = 0; i < this.cardIndexsSame.length; i++)
                if (this.hasSame(card.configId, this.cardIndexsSame[i])) {
                    configId = this.cardIndexsSame.splice(i, 1)[0]
                    break
                }


        let top = this.getTopData(back)

        let selection = this.scene.ten.selection
        if (selection)
            if (
                back.entity == selection.data
                || card.entity == selection.data
                || top.entity == selection.data
            )
                this.scene.ten.removeSelection()


        back.configId = configId
        this.changeState(back, ELogicDataState.card)
        back.entity.startScaleAnim(.3)

        _ui.blockTouchEvent(true)
        _timer.once(this, () => {
            _ui.blockTouchEvent(false)

            // 上下重叠情况
            if (top == card) {
                this.scene.ten.remove(back.entity, card.entity, null!, card.entity)
            }
            else {
                this.scene.ten.remove(back.entity, card.entity, null!, back.entity)

                // 上方移动下来
                back.configId = top.configId
                _logic.ten.changeState(back, ELogicDataState.card)

                back.entity.setPositionXY(top.rect.center, false)
                back.entity.resetItemPos()

                this.changeState(top, ELogicDataState.none)
            }

        }, .3)
    }


    private _propRemove(size: Vector2, out: ILogicData[], type: ELogicDataState) {
        for (let x = 0; x < size.x; x++) {
            // 自上而下
            for (let y = size.y - 1; y >= 0; y--) {
                let index = this.getPosByIndex(x, y, size)
                let data = this.data.datas[index]
                if (data.state == type) {
                    out.push(data)
                    break
                }
            }
        }
    }

    public remove(data1: CubeEntity, data2: CubeEntity, drag: DragEntity, noDown?: CubeEntity) {
        _gameType.callLaterCheckComplete()

        let delay = -1
        if (data1)
            if (!this._remove(data1.data, delay, noDown))
                delay = .2
        if (data2)
            this._remove(data2.data, delay, noDown)

        if (drag) {
            this.dragConfigId = -1
            this.scene.entityMgr.drag.updateView()
        }

        this.removeCount++
        if (this.cardIndexsSame.length > 0)
            if (this.removeCount >= CfirsetSame) {
                this.cardIndexs.push(...this.cardIndexsSame)
                this.cardIndexsSame.length = 0
            }

        _timer.callLater(this, this.checkNo)
    }

    private checkNo() {
        if (!_gameType.isRun)
            return
        if (!this.getSames(1)[0]) {
            if (this.dragCount == 0) {
                _logic.emit(_logic.EventType.FAIL)
            }
            // 下方生出牌
            else
                this.createDrag()
        }
    }

    private _remove(data: ILogicData, ratio: number, noDown?: CubeEntity) {
        this.changeState(data, ELogicDataState.none)
        if (noDown == data.entity)
            return true

        let downData = this.getDownData(data)
        if (downData) {
            if (downData.state == ELogicDataState.back) {
                downData.configId = this.getNextCardId()
                downData.entity.startScaleAnim(.3)
            }
            else if (downData.state == ELogicDataState.cardStatic) {
                this.changeState(downData, ELogicDataState.card)
            }
            return true
        }

        // 拖动下方补牌
        if (this.createDrag()) {
            let dragConfigId = this.dragConfigId
            this.dragConfigId = -1
            _ui.blockTouchEvent(true)
            _timer.once(this, () => {
                _ui.blockTouchEvent(false)
                data.configId = dragConfigId
                this.changeState(data, ELogicDataState.card)
                data.entity.setPositionXY(this.dragCurPos, false)
                data.entity.resetItemPos(true, .1)
                let drag = this.scene.entityMgr.drag
                if (drag)
                    drag.updateView()
            }, ratio)

            return false
        }
        return true
    }


    public createDrag() {
        if (this.dragCount == 0)
            return false

        if (this.dragConfigId == -1) {
            this.dragConfigId = this.getNextCardId()
            this.dragCount--
        }
        if (this.dragCount == 0)
            this.scene.entityMgr.remove(this.scene.entityMgr.drag)
        else
            this.scene.entityMgr.drag.updateView()
        return true
    }

    public getDownData(data: ILogicData) {
        if (data.index.y > 0) {
            let index = this.getPosByIndex(data.index.x, data.index.y - 1, data.isBottom ? this.size_bottom : _logic.size)
            return this.data.datas[index]
        }
        return null!
    }


    public getTopData(data: ILogicData) {
        let size = data.isBottom ? this.size_bottom : _logic.size
        if (data.index.y < size.y) {
            let index = this.getPosByIndex(data.index.x, data.index.y + 1, size)
            return this.data.datas[index]
        }
        return null!
    }

    public propTip(useTip = true) {
        this.scene.clearTips()
        let same = this.getSames(1)[0]

        // 拖动引导
        if (same) {
            let one = same[0],
                two = same[1]

            let drag = this.scene.entityMgr.drag

            if (two == drag) {
                one = two
                two = same[0]
            }

            if (useTip) {
                one.tipAnim()
                two.tipAnim()

                // 手指拖动
                _guide.moveFinger(
                    Vector2.add(v2T, one.getCurPos(), winCenterPostion()),
                    Vector2.add(v2T2, two.getCurPos(), winCenterPostion()),
                )
            }
            return true
        }
        // 对子引导
        let datas: ILogicData[] = []
        for (let data of this.data.datas)
            if (data.state == ELogicDataState.card)
                datas.push(data)

        // for (let data2 of datas) {
        //     for (let _data of datas) {
        //         if (data2 == _data)
        //             continue
        //         if (data2.configId == _data.configId) {
        //             let up = this.getTopData(data2)
        //             let one = _data
        //             if (!up) {
        //                 up = this.getTopData(_data)
        //                 one = data2
        //             }
        //             if (up)
        //                 if (up.state == ELogicDataState.none) {
        //                     _guide.moveFinger(
        //                         Vector2.add(v2T, one.entity.getCurPos(), winCenterPostion()),
        //                         Vector2.add(v2T2, up.entity.getCurPos(), winCenterPostion()),
        //                     )
        //                     return true
        //                 }
        //         }
        //     }
        // }

        let dragId = this.dragConfigId
        if (dragId != -1) {
            // 拖到上方对对
            for (let data of datas) {
                if (data.configId == dragId) {
                    let up = this.getTopData(data)
                    if (up)
                        if (up.state == ELogicDataState.none) {
                            if (useTip)
                                _guide.moveFinger(
                                    Vector2.add(v2T, this.scene.entityMgr.drag.getCurPos(), winCenterPostion()),
                                    Vector2.add(v2T2, up.entity.getCurPos(), winCenterPostion()),
                                )
                            return true
                        }
                }
            }

            // 下方往上拖 空的
            for (let data of this.data.datas) {
                if (data.state == ELogicDataState.none)
                    if (data.index.y == 0) {
                        if (useTip)
                            _guide.moveFinger(
                                Vector2.add(v2T, this.scene.entityMgr.drag.getCurPos(), winCenterPostion()),
                                Vector2.add(v2T2, data.entity.getCurPos(), winCenterPostion()),
                            )
                        return true
                    }
            }
        }

        // 上方往下方拖空的
        // for (let data of this.data.datas) {
        //     if (data.state == ELogicDataState.none)
        //         if (data.index.y == 0) {
        //             for (let _data of this.data.datas) {
        //                 if (_data.state == ELogicDataState.card) {
        //                     _guide.moveFinger(
        //                         Vector2.add(v2T2, _data.entity.getCurPos(), winCenterPostion()),
        //                         Vector2.add(v2T, data.entity.getCurPos(), winCenterPostion()),
        //                     )
        //                     return true
        //                 }
        //             }
        //         }
        // }

        return false
    }

    public changeState(data: ILogicData, state: ELogicDataState) {
        data.state = state

        if (state == ELogicDataState.none)
            data.configId = -1

        data.entity.updateState()
    }

    public getSames(maxCount: number = -1) {
        let datas: ISelectionEntityData[] = []
        for (let data of this.data.datas)
            if (data && data.entity && data.state == ELogicDataState.card)
                datas.push(data.entity)
        let dragId = this.dragConfigId
        if (dragId != -1)
            datas.push(this.scene.entityMgr.drag)

        let sames: ISelectionEntityData[][] = []
        for (let data of datas) {
            if (Sets.filterValue(sames, v => Sets.has(v, data)))
                continue
            for (let _data of datas) {
                if (data == _data)
                    continue
                if (Sets.filterValue(sames, v => Sets.has(v, _data)))
                    continue
                if (this.hasSame(data.getConfigId(), _data.getConfigId())) {
                    sames.push([data, _data])

                    if (maxCount != -1) {
                        if (sames.length >= maxCount)
                            return sames
                    }
                    break
                }
            }
        }

        return sames
    }

    /**可落下的空白位置 */
    public getDragDatas(startData: ISelectionEntityData) {
        let datas: ILogicData[] = []
        this._getDragDatas(_logic.size, datas, startData)
        this._getDragDatas(this.size_bottom, datas, startData)
        return datas
    }

    private _getDragDatas(size: Vector2, out: ILogicData[], startData: ISelectionEntityData) {
        let drag = this.scene.entityMgr.drag
        for (let x = 0; x < size.x; x++) {
            // 自上而下
            for (let y = 0; y < size.y; y++) {
                let index = this.getPosByIndex(x, y, size)
                let data = this.data.datas[index]
                if (startData == data.entity)
                    break
                if (data.state == ELogicDataState.none) {
                    let isAdd = false
                    if (startData == drag)
                        isAdd = true
                    else {
                        // let downData = this.getDownData(data)
                        // if (downData) {
                        //     // if (downData.configId == startData.getConfigId())
                        //     //     isAdd = true
                        // }
                        // else
                        //     isAdd = true
                    }
                    if (isAdd) {
                        out.push(data)
                        break
                    }
                }
            }
        }
    }

    public getNextCardId() {
        let sameIndexs: number[] = []

        // 对对牌
        let sameIndexs2: number[] = []
        let dragId = this.dragConfigId

        let allConfigIds: number[] = []
        if (dragId > 0)
            allConfigIds.push(dragId)

        let sameCounts: { [configId: number]: number } = {}

        for (let data of this.data.datas)
            if (data.state == ELogicDataState.card || data.state == ELogicDataState.cardStatic) {
                if (allConfigIds.indexOf(data.configId) == -1) {
                    allConfigIds.push(data.configId)
                }
                else {
                    if (!sameCounts[data.configId])
                        sameCounts[data.configId] = 1
                    sameCounts[data.configId]++
                }
            }

        for (let dataConfigId of allConfigIds) {
            for (let i = 0; i < this.cardIndexs.length; i++) {
                let configId = this.cardIndexs[i]
                if (this.hasSame(configId, dataConfigId)) {
                    if (!Sets.filterValue(sameIndexs, v => this.cardIndexs[v] == configId))
                        Sets.add(sameIndexs, i)
                }

                if (!Sets.has(this.cubeByTypes[4], configId) && !Sets.has(this.cubeByTypes[4], dataConfigId))
                    if (configId == dataConfigId)
                        if (!sameCounts[dataConfigId])
                            if (!Sets.filterValue(sameIndexs, v => this.cardIndexs[v] == configId))
                                Sets.add(sameIndexs2, i)
            }
        }

        let noSameIndexs: number[] = []
        for (let i = 0; i < this.cardIndexs.length; i++)
            if (!Sets.has(sameIndexs, i))
                noSameIndexs.push(i)

        let index = -1

        // 保证一对可消
        if (this.getSames(1).length == 0) {
            // if (sameIndexs2.length == 0 || this.removeCount <= 5 || Maths.isRandom(.5, _gameType.curRandom.run()))
            index = Sets.random(sameIndexs, null!, () => _gameType.curRandom.run())
            // else
            //     index = Sets.random(sameIndexs2, null!, () => _gameType.curRandom.run())
        }
        // 无用牌随机
        if (index == -1 || index === null! || index === undefined)
            index = Sets.random(noSameIndexs, null!, () => _gameType.curRandom.run())

        if (index == -1 || index === null! || index === undefined)
            index = Sets.randomOf(this.cardIndexs, null!, () => _gameType.curRandom.run())

        let value = this.cardIndexs.splice(index, 1)[0]
        return value
    }

    public hasSame(configId: number, configId2: number) {
        if (configId == -1
            || configId2 == -1)
            return false

        return this.cubeByMerges[configId] == configId2
    }

    public getIndexByAabbCenter(data: ILogicData) {
        _logic.getIndexByAabbCenter(data.rect, data.index, _logic.cubeSize, data.isBottom ? this.offset_bottom : _logic.offset)
    }

    public getPosByIndex(x: number, y: number, size: Vector2) {
        let index = _logic.getPosByIndex(x, y, size)
        if (index == -1)
            return index
        return index + (size == _logic.size ? 0 : _logic.size.x * _logic.size.y)
    }


    /**
      * 显示的牌的索引
      * @param count 场景牌总对数
      * @param isTen 东风 南分
      * @returns count数量*2的 cardids的索引 数组
      */
    public getCardIds(count: number, isTen: boolean, configByIds: number[], sames: number[]) {
        let len = _config_.arr.card_item.length
        if (!isTen)
            len -= this.cubeByTypes[4].length

        for (let i = 0; i < count; i++) {
            let config = _config_.arr.card_item[i % len]

            if (config.num == 5)
                sames.push(config.id)
            else
                configByIds.push(config.id)

            let otherId = this.cubeByMerges[config.id]
            let otherConfig = _config_.obj.card_item[otherId]
            if (otherConfig.num == 5)
                sames.push(otherId)
            else
                configByIds.push(otherId)
        }

        Sets.shuffle(configByIds, true, () => _gameType.curRandom.run())
        Sets.shuffle(sames, true, () => _gameType.curRandom.run())
    }


}